package com.ling.module1sbbyyds.service.chat;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.github.yitter.idgen.YitIdHelper;
import com.ling.sbbyyds.entity.exception.ApiWarningInfoException;
import com.ling.sbbyyds.entity.news.ChatReceiveParams;
import com.ling.sbbyyds.entity.news.ChatUserInfo;
import com.ling.sbbyyds.entity.news.UserChatRoom;
import com.ling.sbbyyds.entity.news.table.NewsChatMessage;
import com.ling.sbbyyds.entity.news.table.NewsChatRoom;
import com.ling.sbbyyds.entity.news.table.NewsUserChat;
import com.ling.sbbyyds.entity.users.User;
import com.ling.module1sbbyyds.service.news.chat.ChatRoomService;
import com.ling.module1sbbyyds.utils.FileLocalStorageTool;
import com.ling.module1sbbyyds.utils.UserThreadLocal;
import com.ling.sbbyyds.attribute.FixedAttribute;
import com.ling.sbbyyds.imagetools.ImageUtils;
import com.ling.sbbyyds.interaction.ResultVo;
import com.ling.sbbyyds.mapper.news.ChatRoomMapper;
import com.ling.sbbyyds.mapper.news.NewsChatMessageMapper;
import com.ling.sbbyyds.mapper.news.NewsChatRoomMapper;
import com.ling.sbbyyds.mapper.news.NewsUserChatMapper;
import com.unfbx.chatgpt.OpenAiClient;
import com.unfbx.chatgpt.entity.completions.Completion;
import com.unfbx.chatgpt.entity.completions.CompletionResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import javax.websocket.Session;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Description: 客户聊天室业务类
 * @author: ling
 * @date: 2023年02月11日
 */
@Slf4j
@Service
public class ClientChatService {
    @Autowired
    private ChatRoomMapper chatRoomMapper;
    @Autowired
    private NewsUserChatMapper newsUserChatMapper;
    @Autowired
    private NewsChatRoomMapper newsChatRoomMapper;
    @Autowired
    private NewsChatMessageMapper newsChatMessageMapper;
    @Autowired
    private FileLocalStorageTool fileLocalStorageTool;
    private final String chatImageFolder = "chatRoomImage";
    private final String chatRoomHeadImageFolder = "chatRoomImage";
    String apiKey = "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";
    OpenAiClient openAiClient = new OpenAiClient(apiKey);
    Random random = new Random();

    /**
     * 接收用户
     *
     * @param chatReceiveParams 客户端发送信息内容
     * @return 请求反馈
     */
    public ResultVo clientSendMsg(ChatReceiveParams chatReceiveParams) {
        ResultVo resultVo;
        // 交互操作
        switch (chatReceiveParams.getOperationType()) {
            // 私聊信息发送
            case "SPR":
                resultVo = this.sendToPrivateChat(chatReceiveParams);
                break;
            //// 群聊信息发送
            case "SGR":
                resultVo = this.sendToGroupChat(chatReceiveParams);
                break;
            default:
                throw new ApiWarningInfoException("参数operationType的值有误,找不到此操作");
        }
        return resultVo;
    }

    // 私聊聊天信息发送
    private ResultVo sendToPrivateChat(ChatReceiveParams params) {
        // 如果聊天室信息记录存在则直接找到聊天记录进行追加，否则
        Long chatRoomId = params.getChatRoomId();
        Long userId = UserThreadLocal.getCurrentUser().getId();
        Long receiveUserId = params.getReceiveUserId();
        String sendContent = params.getSendContent();
        String messageType = params.getMessageType();

        // 调用GPT
        if (this.isCallingRobot("@鼠鼠", params) ||
                this.isCallingRobot("@杰杰", params) ||
                this.isCallingRobot("@手手", params)) {
            return ResultVo.success(this.callingRobot(params));
        }

        // 用户端聊天室不存在，创建聊天室
        NewsChatRoom chatRoom = this.getChatRoom(chatRoomId, userId, receiveUserId);
        chatRoomId = chatRoom.getId();
        // 查询聊天室内所有用户
        List<ChatUserInfo> chatUserInfoList = chatRoomMapper.queryChatRoomUserList(chatRoomId);
        chatUserInfoList.forEach(item -> item.setAvatar(ImageUtils.convertImageUrl(item.getAvatar())));

        // 插入新聊天信息到聊天室
        NewsChatMessage newsChatMessage = new NewsChatMessage()
                .setId(YitIdHelper.nextId())
                .setChatRoomId(chatRoom.getId())
                .setMessageType(messageType)
                .setContent(sendContent)
                .setSendUserId(userId);
        newsChatMessageMapper.insert(newsChatMessage);

        // 向接收者推送信息(如果在线) 推新聊天室
        UserChatRoom userChatRoom = new UserChatRoom()
                .setChatRoomId(chatRoomId)
                .setFriendId(userId)
                .setChatContents(Arrays.asList(newsChatMessage))
                .setChatRoomType(FixedAttribute.CHAT_ROOM_TYPE_PRIVATE)
                .setChatUserList(chatUserInfoList);

        this.pushDataToClient(userChatRoom, new Long[]{receiveUserId}, FixedAttribute.CHAT_MESSAGE_TYPE_ROOM);
        // 修改发送方显示的聊天好友ID
        userChatRoom.setFriendId(receiveUserId);
        this.pushDataToReqUserSessions(userChatRoom, userId, FixedAttribute.CHAT_MESSAGE_TYPE_ROOM);
        return ResultVo.success(userChatRoom);
    }

    /**
     * 调用GPT机器人
     * @param params 请求参数数据
     */
    private UserChatRoom callingRobot(ChatReceiveParams params) {
        Long chatRoomId = params.getChatRoomId();
        // TODO 创建虚拟用户 推送信息给发送端客户
        NewsChatMessage newsChatMessage = new NewsChatMessage()
                .setChatRoomId(chatRoomId)
                .setMessageType(FixedAttribute.CHAT_CONTENT_TYPE_TEXT)
                .setCreateDate(new Date())
                .setContent(this.getGPTMessage(params.getSendContent()))
                // 添加机器人默认头像
                .setSendUserId(null);

        // 向接收者推送信息(如果在线) 推新聊天室
        UserChatRoom userChatRoom = new UserChatRoom()
                .setChatRoomId(chatRoomId)
                .setChatContents(Arrays.asList(newsChatMessage))
                .setChatRoomType(params.getChatRoomType())
                .setChatUserList(null);
        return userChatRoom;
    }

    /**
     * 判断是否包含关键词，包含则将关键词替换为空格
     *
     * @param keyWords    关键词
     * @param params 信息内容
     * @return 是否包含关键词
     */
    private boolean isCallingRobot(String keyWords, ChatReceiveParams params) {
        boolean contains = params.getSendContent().contains(keyWords);
        if (contains) {
            params.setSendContent(params.getSendContent().replace(keyWords, " "));
        }
        return contains;
    }

    /**
     * 获取两用户的私聊聊天室
     *
     * @param chatRoomId    聊天室ID
     * @param userId        用户ID
     * @param receiveUserId 接收用户ID
     * @return 私聊聊天室
     */
    private NewsChatRoom getChatRoom(Long chatRoomId, Long userId, Long receiveUserId) {
        NewsChatRoom newsChatRoom;
        if (!ObjectUtils.isEmpty(chatRoomId)) {
            newsChatRoom = newsChatRoomMapper.selectByPrimaryKey(chatRoomId);
            return newsChatRoom;
        }

        // 查看用户是否有存在的聊天室，有则用没有则创建
        newsChatRoom = newsChatRoomMapper.queryUserMutualPrChatRoom(userId, receiveUserId);
        if (ObjectUtils.isEmpty(newsChatRoom)) {
            newsChatRoom = new NewsChatRoom()
                    .setId(YitIdHelper.nextId())
                    .setChatRoomType(FixedAttribute.CHAT_ROOM_TYPE_PRIVATE);
            newsChatRoomMapper.insert(newsChatRoom);
            // 创建用户与聊天室的关联数据
            this.createRelation(newsChatRoom.getId(), Arrays.asList(userId, receiveUserId));
        }
        return newsChatRoom;
    }

    /**
     * 创建群聊天室
     *
     * @param params 创建群数据
     * @return 操作反馈
     */
    public ResultVo createGroupChatRoom(ChatReceiveParams params) {
        Long[] groupUserId = params.getGroupUserId();
        if (ObjectUtils.isEmpty(groupUserId) || groupUserId.length < 2) {
            throw new ApiWarningInfoException("群用户数量不满足");
        }
        User currentUser = UserThreadLocal.getCurrentUser();
        Long userId = currentUser.getId();
        // 如果存在群头像解析上传群头像
        try {
            if (!ObjectUtils.isEmpty(params.getGroupChatHead())) {
                String path = fileLocalStorageTool.uploadBase64Image(chatRoomHeadImageFolder,
                        params.getGroupChatHead());
                params.setGroupChatHead(path);
            }
        } catch (IOException e) {
            throw new ApiWarningInfoException("头像解析失败");
        }

        // 创建群用户与聊天室的绑定关系
        String chatRoomName = ObjectUtils.isEmpty(params.getGroupChatName())?"新建群聊":params.getGroupChatName();
        NewsChatRoom newsChatRoom = new NewsChatRoom();
        newsChatRoom.setId(YitIdHelper.nextId())
                .setChatRoomType(FixedAttribute.CHAT_ROOM_TYPE_PUBLIC)
                .setChatRoomName(chatRoomName)
                .setChatImagePath(params.getGroupChatHead())
                .setChatRootUserId(userId);
        newsChatRoomMapper.insert(newsChatRoom);
        Long chatRoomId = newsChatRoom.getId();
        // 创建用户与聊天室的关联数据
        List<Long> idList = new ArrayList(Arrays.asList(groupUserId));
        idList.add(userId);
        this.createRelation(chatRoomId, idList);

        // 查询聊天室内所有用户
        List<ChatUserInfo> chatUserInfoList = chatRoomMapper.queryChatRoomUserList(chatRoomId);
        chatUserInfoList.forEach(item -> item.setAvatar(ImageUtils.convertImageUrl(item.getAvatar())));

        UserChatRoom userChatRoom = new UserChatRoom()
                .setChatRoomId(chatRoomId)
                .setChatRoomName(chatRoomName)
                .setChatContents(null)
                .setChatRoomType(FixedAttribute.CHAT_ROOM_TYPE_PUBLIC)
                .setChatUserList(chatUserInfoList);
        // 将信息推送给操作用户的其他设备
        this.pushDataToReqUserSessions(userChatRoom, userId, FixedAttribute.CHAT_MESSAGE_TYPE_ROOM);
        this.pushDataToClient(userChatRoom, new Long[]{chatRoomId}, FixedAttribute.CHAT_MESSAGE_TYPE_ROOM);
        // 向接收者推送信息(如果在线) 推新聊天室
        return ResultVo.success(userChatRoom);
    }

    /**
     * 客户发送群聊信息
     *
     * @param params
     */
    private ResultVo sendToGroupChat(ChatReceiveParams params) {
        // 如果聊天室信息记录存在则直接找到聊天记录进行追加，否则
        Long chatRoomId = params.getChatRoomId();
        Long userId = UserThreadLocal.getCurrentUser().getId();
        String sendContent = params.getSendContent();
        String messageType = params.getMessageType();

        // 调用GPT
        if (this.isCallingRobot("@鼠鼠", params) ||
                this.isCallingRobot("@杰杰", params) ||
                this.isCallingRobot("@手手", params)) {
            return ResultVo.success(this.callingRobot(params));
        }

        // 用户端聊天室不存在，创建聊天室
        NewsChatRoom chatRoom = this.getChatRoom(chatRoomId, userId, null);
        chatRoomId = chatRoom.getId();
        // 插入新聊天信息到聊天室
        NewsChatMessage newsChatMessage = new NewsChatMessage()
                .setId(YitIdHelper.nextId())
                .setChatRoomId(chatRoom.getId())
                .setMessageType(messageType)
                .setContent(sendContent)
                .setSendUserId(userId);
        newsChatMessageMapper.insert(newsChatMessage);

        // 查询聊天室内所有用户
        List<ChatUserInfo> chatUserInfoList = chatRoomMapper.queryChatRoomUserList(chatRoomId);
        chatUserInfoList.forEach(item -> item.setAvatar(ImageUtils.convertImageUrl(item.getAvatar())));

        // 向接收者推送信息(如果在线) 推新聊天室
        UserChatRoom userChatRoom = new UserChatRoom()
                .setChatRoomId(chatRoomId)
                .setChatContents(Arrays.asList(newsChatMessage))
                .setChatRoomType(FixedAttribute.CHAT_ROOM_TYPE_PUBLIC)
                .setChatUserList(chatUserInfoList);

        List<Long> receiveUserId = chatUserInfoList.stream().map(item -> item.getUserId()).collect(Collectors.toList());
        receiveUserId.remove(userId);

        this.pushDataToClient(userChatRoom, receiveUserId.toArray(new Long[0]), FixedAttribute.CHAT_MESSAGE_TYPE_ROOM);
        this.pushDataToReqUserSessions(userChatRoom, userId, FixedAttribute.CHAT_MESSAGE_TYPE_ROOM);
        return ResultVo.success(userChatRoom);
    }

    /**
     * 批量创建聊天室与用户关联
     *
     * @param roomId 聊天室ID
     * @param asList 用户ID列表
     */
    private void createRelation(Long roomId, List<Long> asList) {
        ArrayList<NewsUserChat> newsUserChats = new ArrayList<>(asList.size());
        asList.forEach(item -> {
            NewsUserChat newsUserChat = new NewsUserChat()
                    .setId(YitIdHelper.nextId())
                    .setChatRoomId(roomId)
                    .setUserId(item)
                    .setIsDelete(0);
            newsUserChats.add(newsUserChat);
        });
        newsUserChatMapper.insertList(newsUserChats);
    }

    /**
     * 根据用户会话
     * 推送成功反馈回前端
     *
     * @param data          数据
     * @param userId        用户ID
     * @param serverMesType 信息类型
     */
    private void pushDataToReqUserSessions(Object data, Long userId, String serverMesType) {
        String userToken = UserThreadLocal.getCurrentUserToken();
        JSONObject jsonObject = (JSONObject) JSON.toJSON(data);
        jsonObject.put("serverMesType", serverMesType);
        String messageStr = JSON.toJSONString(ResultVo.success(jsonObject));
        ArrayList<Session> sessions = new ArrayList<>();

        HashMap<String, Session> userSessions = ChatRoomService.webSocketMapUserList.get(userId);
        if (!ObjectUtils.isEmpty(userSessions)) {
            // 排除掉发起请求的会话
            for (String key : userSessions.keySet()) {
                if (!key.equals(userToken)) {
                    sessions.add(userSessions.get(key));
                }
            }
        }
        this.sendMessage(messageStr, sessions);
    }

    /**
     * 根据用户ID
     * 推送成功反馈回前端
     *
     * @param userIds       用户ID
     * @param data          数据
     * @param serverMesType 信息类型
     */
    private void pushDataToClient(Object data, Long[] userIds, String serverMesType) {
        JSONObject jsonObject = (JSONObject) JSON.toJSON(data);
        jsonObject.put("serverMesType", serverMesType);
        String messageStr = JSON.toJSONString(ResultVo.success(jsonObject));
        this.sendMessage(messageStr, userIds);
    }

    /**
     * 根据用户会话
     * 推送成功反馈回前端
     *
     * @param data          数据
     * @param sessions      用户会话
     * @param serverMesType 信息类型
     */
    private void pushDataToClient(Object data, List<Session> sessions, String serverMesType) {
        JSONObject jsonObject = (JSONObject) JSON.toJSON(data);
        jsonObject.put("serverMesType", serverMesType);
        String messageStr = JSON.toJSONString(ResultVo.success(jsonObject));
        this.sendMessage(messageStr, sessions);
    }

    /**
     * 请求GPT模型获取信息
     *
     * @param sendMessage 客户发送信息
     * @return GPT回复信息
     */
    public String getGPTMessage(String sendMessage) {

        int maxToken = random.nextInt(90) + 10;
        float temperature = (random.nextInt(4) + 1) / 10;
        Completion completion = Completion.builder()
                .maxTokens(maxToken)
                .temperature(temperature)
                .prompt(sendMessage)
                .build();

        CompletionResponse completions = openAiClient.completions(completion);
        StringBuilder stringBuilder = new StringBuilder();
        Arrays.stream(completions.getChoices()).forEach(item -> {
            stringBuilder.append(item.getText());
        });
        return stringBuilder.toString().trim();
    }

    /**
     * 服务器主动推送消息 给客户端
     *
     * @param message     信息
     * @param sessionList 会话列表
     */
    private void sendMessage(String message, List<Session> sessionList) {
        sessionList.forEach(item -> {
            try {
                item.getBasicRemote().sendText(message);
            } catch (IOException e) {
                log.error("消息发送失败！", e);
            }
        });
    }

    /**
     * 服务器主动推送消息 给客户端
     *
     * @param message 信息
     * @param userIds 用户列表
     */
    private void sendMessage(String message, Long[] userIds) {
        ArrayList<Session> sessions = new ArrayList<>();
        for (Long userId : userIds) {
            HashMap<String, Session> userSessions = ChatRoomService.webSocketMapUserList.get(userId);
            if (!ObjectUtils.isEmpty(userSessions)) {
                for (String key : userSessions.keySet()) {
                    sessions.add(userSessions.get(key));
                }
            }
        }
        this.sendMessage(message, sessions);
    }

}
